/**
 * File: BufferedRaf.java
 * Created by: mhaimel
 * Created on: 8 Apr 2009
 * CVS:  $Id: BufferedRaf.java,v 1.3 2009/08/13 16:47:05 mhaimel Exp $
 */
package uk.ac.ebi.curtain.utils.io.impl;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * @see http://minddumped.blogspot.com/2009/01/buffered-javaiorandomaccessfile.html
 * @author minddumped.blogspot.com
 */
public class BufferedRaf extends RandomAccessFile {

	public BufferedRaf(File file, String mode) throws FileNotFoundException {
		this(file, mode,65536);
	}
	
	public BufferedRaf(File file, String mode, int buffLen) throws FileNotFoundException {
		super(file, mode);
		_resetBuffer(buffLen);
	}
	
	private void _resetBuffer(int buffLen){
		bufferlength = buffLen;
		bytebuffer = new byte[bufferlength];
		_reset();
	}
	
	private void _reset(){
		maxread = 0;
		buffpos = 0;
		sb = new StringBuilder("0");
	}
	
	public void reset() throws IOException{
		_reset();
		super.seek(0l);
	}
	
	public void resetBufferSize(int buffLen) throws IOException{
		long cPos = _currPos();
		super.seek(cPos);
		if(bufferlength == buffLen){
			_reset();
		} else {
			_resetBuffer(buffLen);
		}
	}

	private byte[] bytebuffer;
	private int bufferlength;
	private int maxread;
	private int buffpos;
	private StringBuilder sb;

	public int getbuffpos() {
		return buffpos;
	}

	@Override
	public int read() throws IOException {
		if (buffpos >= maxread) {
			maxread = readchunk();
			if (maxread == -1) {
				return -1;
			}
		}
		buffpos++;
		return bytebuffer[buffpos - 1];
	}

	public String readLine2() throws IOException {
		sb.delete(0, sb.length());
		int c = -1;
		boolean eol = false;
		while (!eol) {
			switch (c = read()) {
			case -1:
			case '\n':
				eol = true;
				break;
			case '\r':
				eol = true;
				long cur = getFilePointer();
				if ((read()) != '\n') {
					seek(cur);
				}
				break;
			default:
				sb.append((char) c);
				break;
			}
		}

		if ((c == -1) && (sb.length() == 0)) {
			return null;
		}
		return sb.toString();
	}

	@Override
	public long getFilePointer() throws IOException {
		return _currPos();
	}

	@Override
	public void seek(long pos) throws IOException {
		if (maxread != -1 && pos < (super.getFilePointer() + maxread)
				&& pos > super.getFilePointer()) {
			Long diff = (pos - super.getFilePointer());
			if (diff < Integer.MAX_VALUE) {
				buffpos = diff.intValue();
			} else {
				throw new IOException("something wrong w/ seek");
			}
		} else {
			buffpos = 0;
			super.seek(pos);
			maxread = readchunk();
		}
	}

	private int readchunk() throws IOException {
		long pos = _currPos();
		super.seek(pos);
		int read = _fillByteBuffer();
		super.seek(pos);
		buffpos = 0;
		return read;
	}

	protected int _fillByteBuffer() throws IOException {
		return super.read(bytebuffer);
	}

	private long _currPos() throws IOException {
		return super.getFilePointer() + buffpos;
	}
}